package ags.util;

public class FastTrig {
	public static final int DIVISIONS = 72000;
	public static final double K = DIVISIONS/(Math.PI*2);
	public static final float floatK = (float)K;
	public static double[] sineTable;
	public static float[] sineFloatTable;

	public static final void init() {
		if (sineTable == null) {
			sineTable = new double[DIVISIONS];
			sineFloatTable = new float[DIVISIONS];
			for (int i=0; i<DIVISIONS; i++) {
				double value = i/K;
				sineTable[i] = Math.sin(value);
				sineFloatTable[i] = (float)(sineTable[i]);
			}
		}
	}
	
	public static final double sin(double value) {
		return sineTable[(int)(((value*K + 0.5) % DIVISIONS + DIVISIONS)%DIVISIONS)];
	}
	
	public static final double cos(double value) {
		return sineTable[(int)(((value*K + 0.5) % DIVISIONS + 1.25*DIVISIONS)%DIVISIONS)];
	}
	
	public static final float fsin(float value) {
		return sineFloatTable[(int)(((value*floatK + 0.5f) % DIVISIONS + DIVISIONS)%DIVISIONS)];
	}
	
	public static final float fcos(float value) {
		return sineFloatTable[(int)(((value*floatK + 0.5f) % DIVISIONS + 1.25f*DIVISIONS)%DIVISIONS)];
	}
	
	public static void main(String[] args) {
		double v=0;
		long ms;

		ms = -System.nanoTime();
		FastTrig.init();
		ms += System.nanoTime();
		System.out.printf("FastTrig init time: %.5f seconds\n", ms / 1E9);

		int A = 10000;
		int B = 1000;
		double absolute = 0;
		for ( int i = 0; i < A; ++i ) for ( int j = 0; j < B; ++j ) {
			double angle = Math.random() * Math.PI * 200 - Math.PI * 100;
			absolute = Math.max(absolute, Math.abs(Math.sin(angle) - FastTrig.sin(angle)));
			absolute = Math.max(absolute, Math.abs(Math.cos(angle) - FastTrig.cos(angle)));
		}
		System.out.printf("Wrost error: %.12f\n", absolute);
		v=0;
		ms = -System.nanoTime();
		for ( int i = 0; i < A; ++i ) for ( int j = 0; j < B; ++j )
			v += FastTrig.sin(i * j * Math.PI - Math.PI / 3);
		ms += System.nanoTime();
		System.out.printf("FastTrig time: %.3f seconds\n", ms / 1E9);
		v = 0;
		ms = -System.nanoTime();
		for ( int i = 0; i < A; ++i ) for ( int j = 0; j < B; ++j )
			v += Math.sin(i * j * Math.PI - Math.PI / 3);
		ms += System.nanoTime();
		System.out.printf("Math time: %.3f seconds\n", ms/1E9);
		
		// Test float
		absolute = 0;
		for ( int i = 0; i < A; ++i ) for ( int j = 0; j < B; ++j ) {
			float angle = (float)(Math.random() * Math.PI * 200 - Math.PI * 100);
			absolute = Math.max(absolute, Math.abs(Math.sin(angle) - FastTrig.fsin(angle)));
			absolute = Math.max(absolute, Math.abs(Math.cos(angle) - FastTrig.fcos(angle)));
		}
		System.out.printf("Wrost float error: %.12f\n", absolute);
		float v2=0;
		float fPI = (float)Math.PI;
		ms = -System.nanoTime();
		for ( int i = 0; i < A; ++i ) for ( int j = 0; j < B; ++j )
			v2 += FastTrig.fsin((float)(i * j * fPI - fPI / 3));
		ms += System.nanoTime();
		System.out.printf("FastTrig float time: %.3f seconds\n", ms / 1E9);
	}
}